#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "error.h"
#include "lexical.h"

int line_num_g = 1; // 当前行号
int col_num_g = 0;  // 当前字符列位置
char ch_g = ' ';    // 当前字符, 初始值给空格是为了进入 get_token 的 while 循环
char last_ch_g;     // 上一个字符
FILE *file_g = NULL;
symbol_e token_g = INVALID;
char token_id_g[MAX_ID_LEN + 1] = {0};   // 存放标识符
char token_str_g[MAX_STR_LEN + 1] = {0}; // 存放字符串
int token_num_g = 0;                     // 存放数字
char token_ch_g = '\0';                  // 存放字符
void show_ch(char ch); 
void parse_after_double_quot(void);
void parse_after_single_quot(void);
void parse_after_div(void);

symbol_e scanner_get_cur_token(void) { return token_g; }

const char *scanner_get_token_id(void) { return token_id_g; }

const char *scanner_get_token_str(void) { return token_str_g; }

const char scanner_get_token_ch(void) { return token_ch_g; }

const int scanner_get_token_num(void) { return token_num_g; }

const char scanner_get_cur_ch(void) { return ch_g; }

const int scanner_get_line_num(void) { return line_num_g; }

const int scanner_get_col_num(void) { return col_num_g; }

const char *file_name_g = NULL;

void scanner_set_file_and_name(FILE *f, const char *name)
{
    file_g = f;
    file_name_g = name;
}

const char *scanner_get_file_name(void) { return file_name_g; }

// 和 symbol_e 一一对应
const char *syms_names[] = {
    "invalid", "file_end", "comment", "id",     "number", "character",
    "string",  "+",        "-",       "*",      "/",      "%",
    "++",      "--",       "!",       "&",      "&&",     "||",
    "=",       ">",        ">=",      "<",      "<=",     "==",
    "!=",      ",",        ":",       ";",      "(",      ")",
    "[",       "]",        "{",       "}",      "break",  "case",
    "char",    "continue", "default", "do",     "else",   "extern",
    "for",     "if",       "int",     "return", "sizeof", "switch",
    "void",    "while",
};
size_t syms_names_cnt = ARR_ELEM_CNT(syms_names);

// 关键字
static const char *keywords_names[] = {
    "break", "case", "char", "continue", "default", "do",     "else", "extern",
    "for",   "if",   "int",  "return",   "sizeof",  "switch", "void", "while",
};
size_t keywords_names_cnt = ARR_ELEM_CNT(keywords_names);

// 和 rsv_sym_names 的顺序保持一致
static symbol_e keywords[] = {
    BREAK, CASE, CHAR, CONTINUE, DEFAULT, DO,     ELSE, EXTERN,
    FOR,   IF,   INT,  RETURN,   SIZEOF,  SWITCH, VOID, WHILE,
}; 
size_t keywords_cnt = ARR_ELEM_CNT(keywords);




/*
功能：扫描器
返回值：文件结束返回-1, 否则返回0
关联全局变量：line_num_g, col_num_g, ch_g, ch_last_g, file
*/
int scan(void)
{
    static int line_len = 0;
    static int read_pos = 0;
    static char line_buf[MAX_LEN] = {0}; // fread 的行缓冲

    if (!file_g) //文件已经关闭
        return END;

    if (!line_len) {                                    //缓冲区读取完毕
        line_len = fread(line_buf, 1, MAX_LEN, file_g); //重新加载缓冲区数据
        if (line_len == 0) {                            //没有数据了

            line_len = 1;      //结束也当成一个字符
            line_buf[0] = END; //标记文件结束
        }
        read_pos = 0; //恢复读取位置
    }

    last_ch_g = ch_g;
    ch_g = line_buf[read_pos++]; //获取新的字符
   
    line_len--;

    if (last_ch_g == '\n') { //新行
        line_num_g++;        //行号累加
        col_num_g = 1;       //列号复位
    }
    else {
        col_num_g++; //列号递增
    }

    if (ch_g == END) { //文件结束，关闭
        fclose(file_g);
        file_g = NULL;
        return END;
    }

    return 0;
}

void show_ch(char ch) // 用于测试
{
    if (ch == -1)
        printf("EOF");
    else if (ch == '\n')
        printf("\\n");
    else if (ch == '\r')
        printf("\\r");
    else if (ch == '\t')
        printf("\\t");
    else if (ch == ' ')
        printf("<blank>");
    else
        printf("%c", ch);

    printf("\t\t<%d>\n", ch); // 打印ascii码
}

void check_symbol_num(void)
{
    assert(SYMBOL_CNT == syms_names_cnt);
    assert(keywords_cnt == keywords_names_cnt);
}

void check_keyword(const char *token_id)
{
    int start = 0;
    int end = sizeof(keywords_names) / sizeof keywords_names[0] - 1;
    int mid;
    int res;
    while (start <= end) {
        mid = (start + end) / 2; // 折半查找
        res = strcmp(token_id, keywords_names[mid]);
        if (res < 0) {
            end = mid - 1;
        }
        else if (res > 0) {
            start = mid + 1;
        }
        else { // 找到了,是关键字
            token_g = keywords[mid];
            return;
        }
    }
    token_g = ID; //搜索失败，是标识符
}

// 解析标识符（也包括关键字）
void parse_id(void)
{
    int id_cnt = 0;   // 已经读取的标识符长度
    int real_len = 0; // 实际标识符长度
    do {
        real_len++;
        if (id_cnt < MAX_ID_LEN) {
            token_id_g[id_cnt++] = ch_g;
        }
        scan();
    } while (isalnum(ch_g) || (ch_g == '_'));

    token_id_g[id_cnt] = '\0';   // 结尾
    if (real_len > MAX_ID_LEN) { // 标识符过长
        lex_err(ID_2_LONG, WARNING);
    }
    check_keyword(token_id_g); // 检查是否是保留字，这里面会设置 token_g
}

// 解析整数常量
void parse_num(void) // 仅支持十进制
{
    token_g = NUM;
    int num_cnt = 0;
    token_num_g = 0;  // 记录数值
    int real_len = 0; // 实际数字长度

    do {
        real_len++;
        if (num_cnt < MAX_NUM_LEN) {
            token_num_g = 10 * token_num_g + (ch_g - '0');
            num_cnt++;
        }
        scan();
    } while (isdigit(ch_g));

    if (real_len > MAX_NUM_LEN) { // 数字太长
        lex_err(NUM_2_LONG, WARNING);
    }
}



void parse_other_syms(void)
{
    switch (ch_g) {
        case '+':
            token_g = ADD;
            scan();
            if (ch_g == '+') {
                token_g = INC;
                scan();
            }
            return;   

        case '-':
            token_g = SUB;
            scan();
            if (ch_g == '-') {
                token_g = DEC;
                scan();
            }
            return;

        case '*':
            token_g = MUL;
            scan();
            return;

        case '/':
            token_g = DIV;
            scan();
            parse_after_div(); // 里面有 scan
            return;

        case '%':
            token_g = MOD;
            scan();
            return;

        case '>':
            token_g = GT;
            scan();
            if (ch_g == '=') {
                token_g = GE;
                scan();
            }
            return;

        case '<':
            token_g = LT;
            scan();
            if (ch_g == '=') {
                token_g = LE;
                scan();
            }
            return;
   
        case '=':
            token_g = ASSIGN;
            scan();
            if (ch_g == '=') {
                token_g = EQU;
                scan();
            }
            return;

        case '!':
            token_g = NOT;
            scan();
            if (ch_g == '=') {
                token_g = NEQU;
                scan();
            }
            return;

        case '&':
            token_g = LEA;
            scan();
            if (ch_g == '&') {
                token_g = AND;
                scan();
            }
            return;

        case '|':
            token_g = INVALID;
            scan();
            if (ch_g == '|') {
                token_g = OR;
                scan();
            } else {
                lex_err(OR_NO_PAIR, ERROR);
            }
            return;

        case ':':
            token_g = COLON;
            scan();
            return;

        case ';':
            token_g = SEMICO;
            scan();
            return;

        case ',':
            token_g = COMMA;
            scan();
            return;

        case '\'':  // 单引号
            parse_after_single_quot(); // 里面有 scan
            return;

        case '"':
            parse_after_double_quot(); // 里面有 scan
            return;

        case '(':
            token_g = LPAREN;
            scan();
            return;

        case ')':
            token_g = RPAREN;
            scan();
            return;

        case '[':
            token_g = LBRACKET;
            scan();
            return;

        case ']':
            token_g = RBRACKET;
            scan();
            return;

        case '{':
            token_g = LBRACES;
            scan();
            return;

        case '}':
            token_g = RBRACES;
            scan();
            return;

        default:
            token_g = INVALID;
            lex_err(UNRECOGNIZED_TOKEN, ERROR);
            scan();
            return;
    } // switch end
}


#define SET_TOKEN_STR(ch)    \
do\
{\
    real_len++; \
    if(str_cnt < MAX_STR_LEN) \
        token_str_g[str_cnt++] = (ch); \
}while(0)

void parse_after_double_quot(void) // 解析双引号后面的内容
{
    int real_len = 0;
    int str_cnt = 0;
    token_g = INVALID;

    while (1) {
        scan();
        if(ch_g == END || ch_g == '\n') {
            lex_err(STR_INCOMPLT, ERROR);
            return;
        }
        if (ch_g == '"')
            break;
        if (ch_g != '\\') { // 非转义符
            SET_TOKEN_STR(ch_g);
        }
        else { // 转义符
            scan();
            if(ch_g == END) {
                lex_err(STR_INCOMPLT, ERROR);
                return;
            } 
            switch (ch_g) {
                case 'n':
                    SET_TOKEN_STR('\n');
                    break;
                case 't':
                    SET_TOKEN_STR('\t');
                    break;
                case '0':
                    SET_TOKEN_STR('\0');
                    break;
                case '\\':
                    SET_TOKEN_STR('\\');
                    break;
                case '"':
                    SET_TOKEN_STR('"');
                    break;
                case '\n':
                    // 什么也不做，字符串续行(\+换行)
                    break;
                default:
                    SET_TOKEN_STR(ch_g);
                    lex_err(UNKNOW_ESCAPE_SEQUENCE, WARNING);                  
                    break;
            }
        }
    }

    token_str_g[str_cnt] = '\0';  // 结尾
    if (real_len > MAX_STR_LEN) { // string 太长
        lex_err(STR_2_LONG, WARNING);
    }
    token_g = STRING;
    scan();
    return;
}

void parse_after_single_quot(void) // 解析单引号后面的内容
{
    token_g = INVALID;
    scan();
    if(ch_g == END || ch_g == '\n') {
        lex_err(CHARA_INCOMPLT, ERROR);
        return;
    } else if(ch_g == '\'') {
        lex_err(EMPTY_CHARA, ERROR);
        scan();
        return;
    } else if(ch_g == '\\') {  // 转义符
        scan(); 
        if(ch_g == END || ch_g == '\n') {
            lex_err(CHARA_INCOMPLT, ERROR);
            return;
        }
        switch (ch_g) {
            case 'n':
                token_ch_g = '\n';
                break;
            case '\\':
                token_ch_g = '\\';
                break;
            case 't':
                token_ch_g = '\t';
                break;
            case '0':
                token_ch_g = '\0';
                break;
            case '\'':
                token_ch_g = '\'';
                break;

            default: // 不知道的转义
                lex_err(UNKNOW_ESCAPE_SEQUENCE, WARNING);
                token_ch_g = ch_g; 
                break;
        }
    } else { // 普通字符
        token_ch_g = ch_g; 
    }

    scan();
    if (ch_g == '\'') {
        token_g = CHARA;
        scan();
        return;
    } 

    lex_err(CHARA_INCOMPLT, ERROR);
    return;
}


void parse_after_div(void) 
{
    if (ch_g == '/') {     // 连续2个/
        token_g = COMMENT; // 单行注释

        while(1) {
            scan();
            if(ch_g == '\n') {
                scan();
                return;
            } else if(ch_g == END) {
                return;
            }
        }
    }
    else if (ch_g == '*') { // 多行注释
        token_g = INVALID;        
        while (1) { // s2
            scan();
            if (ch_g == END) {
                lex_err(COMMENT_NO_END, ERROR);
                return;
            } else if (ch_g == '*') {            
                while(1) { // s4
                    scan();
                    if (ch_g == '*') {
                        ;
                    } else if (ch_g == END) {
                        lex_err(COMMENT_NO_END, ERROR);
                        return;
                    } else if (ch_g == '/') {
                        token_g = COMMENT;
                        scan();
                        return;
                    } else {
                        break; // -> s2
                    }                    
                }
            }         
        } 
    } else {
        return; // 除号
    }
}

/*
    每调用一次，得到一个词法记号，保存在 token_g 中
*/
void get_token(void)
{
    while (isspace(ch_g)) { // 忽略空白字符   sspace()用于检测字符是否为空白字符，包括：空格，'\t' '\n' '\r' 垂直制表符 ('\v') 换页符 ('\f')
        scan();
    }  

    if (ch_g == END) {
        token_g = FILE_END; // 文件结束
        return;
    }

    if (isalpha(ch_g) || (ch_g == '_')) {
        parse_id();
    }
    else if (isdigit(ch_g)) {
        parse_num();
    }
    else {
        parse_other_syms();
    }
}




void show_token(symbol_e token)
{	
	switch(token){
		case STRING:
			printf("字符串常量\t\"%s\"\n", scanner_get_token_str());
			break;
						
		case NUM:
			printf("数字常量\t%d\n", scanner_get_token_num());
			break;
			
		case CHARA:
			printf("字符常量\t\'%c\'\n", scanner_get_token_ch());
			break;
			
		case ID:
			printf("标识符\t\t%s\n", scanner_get_token_id());
			break;			
            
		case BREAK ... WHILE:
			printf("关键字\t\t%s\n", scanner_get_token_id());
			break;	
            
		case ADD ... NEQU: //注意...两边都有空格
			printf("运算符\t\t\%s\n", syms_names[token_g]);
			break;
		case COMMA ... RBRACES://注意...两边都有空格
			printf("界符\t\t\%s\n", syms_names[token_g]);
			break;
			
		default:

			printf("其他\t\t%s\n", syms_names[token_g]);
			break;
	}
	
}

void show_ch_including_escape(char ch)
{
	if(ch == '\n')
		printf("\\n");
	
	else if(ch=='\r')
		printf("\\r");	
	
	else if(ch=='\t')
		printf("\\t");	
	
	else if(ch == '\0')
		printf("\\0");
	
	else 
		printf("%c", ch);
}

void show_color_token(symbol_e token)
{
	switch(token){
		case STRING:
			printf(HI_RED"\"");
			int i = 0;
			const char *p = scanner_get_token_str();
			for(; i < strlen(scanner_get_token_str()); ++i)
				show_ch_including_escape(p[i]);
			printf("\"");
			break;
						
		case NUM:
			printf(HI_CYA"%d", scanner_get_token_num());
			break;
			
		case CHARA:
			printf(HI_PUR"\'");			
			show_ch_including_escape(scanner_get_token_ch());
			printf("\'");
			break;
			
		case ID:
			printf(HI_WHI"%s", scanner_get_token_id());
			break;			
            
		case BREAK ... WHILE:
			printf(HI_YEL"%s", scanner_get_token_id());
			break;	
            
		case ADD ... NEQU: //注意...两边都有空格
			printf(HI_GRE"%s", syms_names[scanner_get_cur_token()]);
			break;
		case COMMA ... RBRACES://注意...两边都有空格
			printf(HI_BLU"%s", syms_names[scanner_get_cur_token()]);
			break;	
		default:
			break;
	}
	printf(CLOSE);	
}